use derive_builder::Builder;
use serde::{Deserialize, Serialize};

use crate::error::Error;
use crate::mapper::ValueMapper;
use crate::modeling::{
    ClassAttributePersistence, ClassPersistence, ClassPersistenceBuilder, ClassPersistenceGetter,
    Value, ValueSize, ValueType,
};

use super::{Class, ClassAttributeBuilder, ClassBuilder, ClassGetter};

const NAMESPACE_CLASS_CLASS_IDENTIFIER: &'static str = "g01_namespace_class";

const NAMESPACE_CLASS_CLASS_NAME: &'static str = "namespace_class";

const NAMESPACE_CLASS_TABLE_NAME: &'static str = "_g01_meta_namespace_classes";

#[derive(Debug, Serialize, Deserialize, Default, Clone, Builder, PartialEq)]
#[builder(name = "NameSpaceClassRelationBuilder")]
#[builder(pattern = "mutable")]
#[builder(setter(into, strip_option), default)]
#[builder(derive(Debug))]
#[builder(build_fn(error = "Error"))]
pub struct NameSpaceClassRelation {
    /// id
    pub id: i64,
    /// domain identifier
    pub namespace_identifier: String,
    /// identity
    pub class_identifier: String,
    /// 选项
    pub options: Option<String>,
    /// 备注
    pub description: String,
    /// 创建时间
    pub created_at: i64,
    /// 更新时间
    pub updated_at: i64,
}

impl ClassPersistenceGetter for NameSpaceClassRelation {
    fn table_name() -> anyhow::Result<String> {
        Ok(String::from(NAMESPACE_CLASS_TABLE_NAME))
    }
    fn class_persistence() -> anyhow::Result<Option<ClassPersistence>> {
        let mut builder = ClassPersistenceBuilder::default();
        let mut attr_persistences: Vec<ClassAttributePersistence> = vec![];
        if let Some(attributes) = &NameSpaceClassRelation::get_class()?.attributes {
            for attribute in attributes {
                //
                attr_persistences.push(ClassAttributePersistence::try_from(attribute)?);
            }
        } else {
        }
        builder
            .table_name(NameSpaceClassRelation::table_name()?)
            .columns(attr_persistences);
        Ok(Some(builder.build()?))
    }
}

impl ClassGetter for NameSpaceClassRelation {
    fn get_name() -> anyhow::Result<String> {
        Ok(String::from(NAMESPACE_CLASS_CLASS_NAME))
    }
    fn get_identifier() -> anyhow::Result<String> {
        Ok(String::from(NAMESPACE_CLASS_CLASS_IDENTIFIER))
    }
    fn get_class() -> anyhow::Result<Class> {
        let mut attribute_builder = ClassAttributeBuilder::default();
        attribute_builder.class_identifier(NAMESPACE_CLASS_CLASS_IDENTIFIER);

        let mut append = vec![
            attribute_builder
                .clone()
                .name("id")
                .title("ID")
                .position(0)
                .data_type(ValueType::Number)
                .build()?,
            attribute_builder
                .clone()
                .name("namespace_identifier")
                .title("命名空间")
                .position(11)
                .is_key(true)
                .data_type(ValueType::String)
                .build()?,
            attribute_builder
                .clone()
                .name("class_identifier")
                .title("类型")
                .position(12)
                .is_key(true)
                .data_type(ValueType::String)
                .build()?,
            attribute_builder
                .clone()
                .name("description")
                .title("描述")
                .position(991)
                .data_type(ValueType::String)
                .value_size(ValueSize::Normal)
                .build()?,
            attribute_builder
                .clone()
                .name("options")
                .title("选项")
                .position(992)
                .data_type(ValueType::String)
                .build()?,
            attribute_builder
                .clone()
                .name("created_at")
                .title("创建时间")
                .position(993)
                .data_type(ValueType::Timestamp)
                .build()?,
            attribute_builder
                .clone()
                .name("updated_at")
                .title("更新时间")
                .position(994)
                .data_type(ValueType::Timestamp)
                .build()?,
        ];
        append.sort_by(|a, b| a.position.cmp(&b.position));

        let clazz = ClassBuilder::default()
            .identifier(NAMESPACE_CLASS_CLASS_IDENTIFIER)
            .name(NAMESPACE_CLASS_CLASS_NAME)
            .data_is_type(false)
            .is_persistent(true)
            .primary_key(String::from("namespace_identifier,class_identifier"))
            .attributes(append)
            .build()?;
        Ok(clazz)
    }
}

impl TryFrom<Value> for NameSpaceClassRelation {
    type Error = anyhow::Error;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        let class = Self::get_class()?;
        let v: serde_json::Value = class.to_value(&class, &value)?.try_into()?;
        let res = serde_json::from_value(v).map_err(|err| {
            tracing::debug!("{}", err);
            anyhow::anyhow!("{}", err)
        })?;
        Ok(res)
    }
}

impl TryInto<Value> for NameSpaceClassRelation {
    type Error = anyhow::Error;

    fn try_into(self) -> Result<Value, Self::Error> {
        Ok(serde_json::to_value(self)
            .map_err(|err| anyhow::anyhow!("{}", err))?
            .try_into()?)
    }
}
